package main

import (
	"crypto/subtle"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"gopkg.in/yaml.v3"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// NewDockerComposeManager 创建并初始化DockerComposeManager实例
func NewDockerComposeManager(defaultFilePath, authToken string, logger *logrus.Logger) *DockerComposeManager {
	return &DockerComposeManager{
		defaultFilePath: defaultFilePath,
		authToken:       authToken,
		logger:          logger,
		serviceHistory: &ServiceHistory{
			versions: make(map[string][]string),
		},
		cmdExecutor: &RealCommandExecutor{},
	}
}

// handleError 统一错误处理
func (m *DockerComposeManager) handleError(c *gin.Context, statusCode int, err error, message string) {
	m.logger.WithError(err).Error(message)
	c.AbortWithStatusJSON(statusCode, gin.H{"error": fmt.Sprintf("%s: %v", message, err)})
}

// validateToken 验证API请求令牌（使用恒定时间比较算法防时序攻击）
func (m *DockerComposeManager) validateToken(c *gin.Context) bool {
	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		return false
	}
	return subtle.ConstantTimeCompare([]byte(authHeader), []byte("Bearer "+m.authToken)) == 1
}

// validateServiceName 验证服务名称合法性
func validateServiceName(name string) error {
	if name == "" {
		return errors.New("service name cannot be empty")
	}
	if strings.ContainsAny(name, "/\\") {
		return errors.New("invalid service name")
	}
	return nil
}

func (m *DockerComposeManager) validateFilePath(path string) error {
	if path == "" {
		return errors.New("file path cannot be empty")
	}
	// 根据需要添加额外的路径验证
	return nil
}

func (m *DockerComposeManager) getFilePath(requestPath string) string {
	if requestPath != "" {
		return requestPath
	}
	return m.defaultFilePath
}

func (m *DockerComposeManager) parseRequest(c *gin.Context, info *updateInfo) error {
	contentType := c.GetHeader("Content-Type")
	switch {
	case strings.Contains(contentType, "application/json"):
		if err := c.BindJSON(info); err != nil {
			return fmt.Errorf("invalid JSON format: %w", err)
		}
	case strings.Contains(contentType, "application/x-www-form-urlencoded"):
		if err := c.ShouldBind(info); err != nil {
			return fmt.Errorf("invalid form data: %w", err)
		}
	default:
		return errors.New("unsupported Content-Type")
	}
	return nil
}

// getDockerVersion 获取Docker服务版本（带缓存机制）
func (m *DockerComposeManager) getDockerVersion() (float64, error) {
	if m.versionChecked {
		return m.dockerVersion, nil
	}

	out, err := m.cmdExecutor.Execute("docker", "version", "--format", "{{.Server.Version}}")
	if err != nil {
		return 0, fmt.Errorf("failed to get Docker version: %w", err)
	}

	versionStr := strings.Split(string(out), ".")[0]
	version, err := strconv.ParseFloat(versionStr, 64)
	if err != nil {
		return 0, fmt.Errorf("failed to parse Docker version: %w", err)
	}

	m.dockerVersion = version
	m.versionChecked = true
	return version, nil
}

// pullImage 拉取指定的Docker镜像
func (m *DockerComposeManager) pullImage(image string) error {
	m.logger.Infof("Pulling image: %s", image)
	_, err := m.cmdExecutor.Execute("docker", "pull", image)
	return err
}

// readAndParseYAML 读取并解析YAML格式的compose文件
func (m *DockerComposeManager) readAndParseYAML(filePath string) (*yaml.Node, error) {
	data, err := os.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read docker-compose file: %w", err)
	}

	var content yaml.Node
	if err := yaml.Unmarshal(data, &content); err != nil {
		return nil, fmt.Errorf("failed to unmarshal YAML content: %w", err)
	}

	return &content, nil
}

// updateImageInYAML 在YAML节点树中更新指定服务的镜像版本
func (m *DockerComposeManager) updateImageInYAML(content *yaml.Node, serviceName, newImage string) (string, error) {
	found := false
	var currentImage string

	for i, node := range content.Content[0].Content {
		if node.Value == "services" {
			servicesNode := content.Content[0].Content[i+1]
			for j := 0; j < len(servicesNode.Content); j += 2 {
				serviceNode := servicesNode.Content[j]
				if serviceNode.Kind == yaml.ScalarNode && serviceNode.Value == serviceName {
					for k := 0; k < len(servicesNode.Content[j+1].Content); k += 2 {
						keyNode := servicesNode.Content[j+1].Content[k]
						if keyNode.Value == "image" {
							valueNode := servicesNode.Content[j+1].Content[k+1]
							currentImage = valueNode.Value
							valueNode.Value = newImage
							found = true
							break
						}
					}
				}
				if found {
					break
				}
			}
			if found {
				break
			}
		}
	}

	if !found {
		return "", fmt.Errorf("service %s not found in docker-compose file", serviceName)
	}

	return currentImage, nil
}

func (m *DockerComposeManager) writeYAML(filePath string, content *yaml.Node) error {
	updatedData, err := yaml.Marshal(content)
	if err != nil {
		return fmt.Errorf("failed to marshal YAML content: %w", err)
	}

	finalContent := strings.ReplaceAll(string(updatedData), "    ", "  ")
	return os.WriteFile(filePath, []byte(finalContent), 0644)
}

func (m *DockerComposeManager) updateServiceHistory(serviceName, currentImage string) {
	const maxHistory = 5

	m.serviceHistory.Lock()
	defer m.serviceHistory.Unlock()

	if _, exists := m.serviceHistory.versions[serviceName]; !exists {
		m.serviceHistory.versions[serviceName] = []string{}
	}

	if len(m.serviceHistory.versions[serviceName]) >= maxHistory {
		m.serviceHistory.versions[serviceName] = m.serviceHistory.versions[serviceName][1:]
	}

	m.serviceHistory.versions[serviceName] = append(m.serviceHistory.versions[serviceName], currentImage)
}

// composeUp 执行docker-compose up命令重启服务
func (m *DockerComposeManager) composeUp(filePath, serviceName string) error {
	version, err := m.getDockerVersion()
	if err != nil {
		return err
	}

	var cmd []string
	if _, err := exec.LookPath("docker-compose"); err == nil {
		cmd = []string{"docker-compose", "-f", filePath, "up", "-d", serviceName}
	} else if version >= 24 {
		cmd = []string{"docker", "compose", "-f", filePath, "up", "-d", serviceName}
	} else {
		return errors.New("neither docker-compose nor docker compose is available")
	}

	_, err = m.cmdExecutor.Execute(cmd[0], cmd[1:]...)
	return err
}

// UpdateDockerCompose 更新docker-compose文件中指定服务的镜像版本
func (m *DockerComposeManager) UpdateDockerCompose(c *gin.Context) {
	if !m.validateToken(c) {
		m.handleError(c, http.StatusUnauthorized, nil, "Invalid token")
		return
	}

	var info updateInfo
	if err := m.parseRequest(c, &info); err != nil {
		m.handleError(c, http.StatusBadRequest, err, "Invalid request")
		return
	}

	if err := validateServiceName(info.Service); err != nil {
		m.handleError(c, http.StatusBadRequest, err, "Invalid service name")
		return
	}

	filePath := m.getFilePath(info.FilePath)
	if err := m.validateFilePath(filePath); err != nil {
		m.handleError(c, http.StatusBadRequest, err, "Invalid file path")
		return
	}

	if err := m.pullImage(info.Image); err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to pull image")
		return
	}

	content, err := m.readAndParseYAML(filePath)
	if err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to read compose file")
		return
	}

	currentImage, err := m.updateImageInYAML(content, info.Service, info.Image)
	if err != nil {
		m.handleError(c, http.StatusNotFound, err, "Service not found")
		return
	}

	if currentImage != "" {
		m.updateServiceHistory(info.Service, currentImage)
	}

	if err := m.writeYAML(filePath, content); err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to write compose file")
		return
	}

	if err := m.composeUp(filePath, info.Service); err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to update service")
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Service updated successfully"})
}

// RollbackService 处理回滚服务的POST请求
func (m *DockerComposeManager) RollbackService(c *gin.Context) {
	// 认证检查
	if !m.validateToken(c) {
		m.handleError(c, http.StatusUnauthorized, nil, "Invalid token")
		return
	}

	// 绑定JSON请求体
	var req struct {
		Service  string `json:"service" binding:"required"`
		FilePath string `json:"filePath"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		m.handleError(c, http.StatusBadRequest, err, "Invalid request body")
		return
	}

	// 参数验证
	if err := validateServiceName(req.Service); err != nil {
		m.handleError(c, http.StatusBadRequest, err, "Invalid service name")
		return
	}

	filePath := m.getFilePath(req.FilePath)
	if err := m.validateFilePath(filePath); err != nil {
		m.handleError(c, http.StatusBadRequest, err, "Invalid file path")
		return
	}

	// 获取历史版本
	m.serviceHistory.Lock()
	versions, exists := m.serviceHistory.versions[req.Service]
	if !exists || len(versions) == 0 {
		m.serviceHistory.Unlock()
		m.handleError(c, http.StatusNotFound, nil, "No previous version found")
		return
	}
	previousImage := versions[len(versions)-1]
	m.serviceHistory.Unlock()

	// 读取并修改compose文件（使用与UpdateDockerCompose相同的处理逻辑）
	content, err := m.readAndParseYAML(filePath)
	if err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to read compose file")
		return
	}

	if _, err := m.updateImageInYAML(content, req.Service, previousImage); err != nil {
		m.handleError(c, http.StatusNotFound, err, "Service not found")
		return
	}

	// 使用与UpdateDockerCompose相同的YAML写入逻辑
	updatedData, err := yaml.Marshal(content)
	if err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to marshal YAML content")
		return
	}

	// 保持与更新操作相同的缩进格式（2空格）
	finalContent := strings.ReplaceAll(string(updatedData), "    ", "  ")
	if err := os.WriteFile(filePath, []byte(finalContent), 0644); err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to write compose file")
		return
	}

	// 使用相同的composeUp方法重启服务
	if err := m.composeUp(filePath, req.Service); err != nil {
		m.handleError(c, http.StatusInternalServerError, err, "Failed to rollback service")
		return
	}

	// 更新历史记录
	m.serviceHistory.Lock()
	m.serviceHistory.versions[req.Service] = versions[:len(versions)-1]
	m.serviceHistory.Unlock()

	c.JSON(http.StatusOK, gin.H{
		"status":  "success",
		"service": req.Service,
		"image":   previousImage,
		"message": fmt.Sprintf("Service %s rolled back to %s", req.Service, previousImage),
	})
}

// HealthCheck 服务健康检查接口
func (m *DockerComposeManager) HealthCheck(c *gin.Context) {
	if _, err := m.cmdExecutor.Execute("docker", "version"); err != nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"status": "unhealthy"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "healthy"})
}

// Execute 执行系统命令的标准方法
func (e *RealCommandExecutor) Execute(name string, arg ...string) ([]byte, error) {
	return exec.Command(name, arg...).CombinedOutput() // 合并标准输出和错误
}
